﻿using AMAP.Areas.Admin.Models.Basket;
using AMAP.Domain;
using AMAP.ServiceInterfaces;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Web.Mvc;
using ExternalOffer = AMAP.Areas.Admin.Models.Basket.AdminBasketCreateViewModel.ExternalOffer;
using ExternalResponse = AMAP.Areas.Admin.Models.Basket.AdminBasketCreateViewModel.ExternalResponse;
using OfferResponse = AMAP.Areas.Admin.Models.Basket.AdminBasketCreateViewModel.OfferResponse;
using StockItem = AMAP.Areas.Admin.Models.Basket.AdminBasketCreateViewModel.StockItem;
using StockResponse = AMAP.Areas.Admin.Models.Basket.AdminBasketCreateViewModel.StockResponse;

namespace AMAP.Areas.Admin.Controllers
{
    public class BasketController : Controller
    {
        private IAmapService AmapService;
        private IBasketOrderService BasketOrderService;
        private IBasketService BasketService;
        private ICompositionService CompositionService;
        private IDeliveryService DeliveryService;
        private IExternalCompositionService ExternalCompositionService;
        private IExternalOrderService ExternalOrderService;
        private IOfferService OfferService;
        private IOrderService OrderService;
        private IOtherAmapService OtherAmapService;
        private IProductService ProductService;
        private IRequestService RequestService;
        private ISubscriptionService SubscriptionService;

        public BasketController(IAmapService AmapService, IBasketOrderService BasketOrderService, IBasketService BasketService, ICompositionService CompositionService, IDeliveryService DeliveryService, IExternalCompositionService ExternalCompositionService, IExternalOrderService ExternalOrder, IOfferService OfferService, IOrderService OrderService, IOtherAmapService OtherAmapService, IProductService ProductService, IRequestService RequestService, ISubscriptionService SubscriptionService)
        {
            this.AmapService = AmapService;
            this.BasketOrderService = BasketOrderService;
            this.BasketService = BasketService;
            this.CompositionService = CompositionService;
            this.DeliveryService = DeliveryService;
            this.ExternalCompositionService = ExternalCompositionService;
            this.ExternalOrderService = ExternalOrder;
            this.OrderService = OrderService;
            this.OfferService = OfferService;
            this.OtherAmapService = OtherAmapService;
            this.ProductService = ProductService;
            this.RequestService = RequestService;
            this.SubscriptionService = SubscriptionService;
        }


        //
        // GET: /Admin/Basket/

        public ActionResult Index()
        {
            return View();
        }

        [Authorize(Roles = "Preparateur")]
        public ActionResult Composition(int DayOfWeek = 0)
        {
            AdminBasketCompositionViewModel Model = new AdminBasketCompositionViewModel();
            Model.DayOfWeek = DayOfWeek;
            InitModel(Model);
            return View(Model);
        }

        private void InitModel(AdminBasketCompositionViewModel Model)
        {
            DateTime Week = DateTime.Now.AddDays(0);
            Model.NumberOfBasket = this.BasketService.GetNumberOfBasket(Week, (DayOfWeek)Model.DayOfWeek);
            Basket Basket = this.BasketService.GetByWeek(Week);
            Model.Items = new List<BasketItem>();
            if (Basket != null)
            {
                foreach (ExternalComposition e in this.ExternalCompositionService.GetListByBasketId(Basket.Id))
                    Model.Items.Add(new BasketItem
                    {
                        Name = e.ExternalOrder.Product.Name,
                        Quantity = e.Quantity,
                        Unit = e.ExternalOrder.Unit
                    });
                foreach (Composition c in this.CompositionService.GetListByBasketId(Basket.Id))
                    Model.Items.Add(new BasketItem
                    {
                        Name = c.Order.Offer.Product.Name,
                        Quantity = c.Quantity,
                        Unit = c.Order.Offer.Unit
                    });
            }
        }


        private void InitModel(AdminBasketCreateViewModel Model)
        {
            // Lists
            if (Model.Stock == null)
                Model.Stock = new List<StockItem>();
            Model.NewOffers = new List<Offer>();
            Model.ExternalOffers = new List<ExternalOffer>();

            // Miscellaneous variables
            Model.NumberOfBasket = BasketService.GetNumberOfBasket(DateTime.Now);
        }

        [Authorize(Roles = "Compositeur")]
        public ActionResult Create()
        {
            AdminBasketCreateViewModel Model = new AdminBasketCreateViewModel(OrderService.GetStock(), ExternalOrderService.GetStock());
            InitModel(Model);

            Model.NewOffers = this.OfferService.GetLastValidOffers();
            Model.ExternalOffers = this.OtherAmapService.GetLastValidOffers();


            return View(Model);
        }

        [HttpPost]
        [Authorize(Roles = "Compositeur")]
        public ActionResult Create(AdminBasketCreateViewModel Model)
        {
            InitModel(Model);

            CultureInfo Culture = Thread.CurrentThread.CurrentCulture;
            Basket BasketItem = new Basket
            {
                Price = Model.BasketPrice ?? 10,
                Week = (short)Culture.Calendar.GetWeekOfYear(DateTime.Now, Culture.DateTimeFormat.CalendarWeekRule, Culture.DateTimeFormat.FirstDayOfWeek),
                Year = (short)DateTime.Now.Year
            };
            this.BasketService.Add(BasketItem);

            // Compute composition from stock
            foreach (StockResponse StockItem in Model.StockUsage.Where(s => s.Quantity > 0))
            {
                if (Type.GetType(StockItem.Type) == typeof(Order))
                {
                    Order OrderItem = OrderService.GetById(StockItem.StockId);
                    if (OrderItem.RemainedQuantity - StockItem.Quantity >= 0)
                    {
                        // Update stock levels
                        OrderItem.RemainedQuantity -= StockItem.Quantity;
                        this.OrderService.Update(OrderItem);
                        // Save basket line
                        Composition CompositionItem = new Composition
                        {
                            BasketId = BasketItem.Id,
                            OrderId = OrderItem.Id,
                            Quantity = StockItem.Quantity / (double)Model.NumberOfBasket
                        };
                        this.CompositionService.Add(CompositionItem);
                    }
                }
                else
                {
                    ExternalOrder ExternalOrderItem = this.ExternalOrderService.GetById(StockItem.StockId);
                    if (ExternalOrderItem.RemainedQuantity - StockItem.Quantity > 0)
                    {
                        // Update stock levels
                        ExternalOrderItem.RemainedQuantity -= StockItem.Quantity;
                        this.ExternalOrderService.Update(ExternalOrderItem);
                        // Save basket line
                        ExternalComposition ExternalCompositionItem = new ExternalComposition
                        {
                            BasketId = BasketItem.Id,
                            ExternalOrderId = ExternalOrderItem.Id,
                            Quantity = StockItem.Quantity / (double)Model.NumberOfBasket
                        };
                        this.ExternalCompositionService.Add(ExternalCompositionItem);
                    }
                }
            }
            this.CompositionService.Save();

            // Compute composition from suppliers
            foreach (OfferResponse OfferItem in Model.OfferUsage.Where(o => o.Unit > 0 || o.Quantity > 0))
            {
                Offer CurrentOffer = OfferService.GetById(OfferItem.OfferId);
                if (OfferItem.Quantity > 0 || OfferItem.Unit > 0)
                {
                    double NeededQuantity;
                    double Used = Model.NumberOfBasket * OfferItem.Unit;
                    if (Used >= OfferItem.Quantity)
                        if (Used > CurrentOffer.Quantity)
                        {
                            NeededQuantity = CurrentOffer.Quantity;
                            OfferItem.Unit = NeededQuantity / Model.NumberOfBasket;
                        }
                        else
                            NeededQuantity = Used;
                    else
                        if (OfferItem.Quantity > CurrentOffer.Quantity)
                            NeededQuantity = CurrentOffer.Quantity;
                        else
                            NeededQuantity = OfferItem.Quantity;

                    // Save Amap request with maximal priority
                    Request RequestItem = new Request
                    {
                        AmapId = AmapService.GetApplicationAMAP().Id,
                        OfferId = CurrentOffer.Id,
                        ProvidedQuantity = NeededQuantity,
                        WantedQuantity = NeededQuantity
                    };
                    this.RequestService.Add(RequestItem);

                    // Insert supplier order
                    Order OrderItem = new Order
                    {
                        Id = CurrentOffer.Id,
                        DeliveredQuantity = NeededQuantity,
                        OrderedQuantity = NeededQuantity,
                        RemainedQuantity = NeededQuantity - Used
                    };
                    this.OrderService.Add(OrderItem);

                    if (OfferItem.Unit > 0)
                    {
                        // Save basket line
                        Composition CompositionItem = new Composition
                        {
                            BasketId = BasketItem.Id,
                            OrderId = OrderItem.Id,
                            Quantity = OfferItem.Unit
                        };
                        this.CompositionService.Add(CompositionItem);
                    }
                }
            }
            this.RequestService.Save();


            // Compute composition from other AMAP
            foreach (ExternalResponse ExternalItem in Model.ExternalUsage.Where(e => e.Unit > 0 || e.Quantity > 0))
            {
                ExternalOffer CurrentOffer = this.OtherAmapService.GetById(ExternalItem.AmapId, ExternalItem.OfferId);
                if (ExternalItem.Quantity > 0 || ExternalItem.Unit > 0)
                {
                    double NeededQuantity;
                    double Used = Model.NumberOfBasket * ExternalItem.Unit;
                    if (Used >= ExternalItem.Quantity)
                        if (Used > CurrentOffer.Quantity)
                        {
                            NeededQuantity = CurrentOffer.Quantity;
                            ExternalItem.Unit = NeededQuantity / Model.NumberOfBasket;
                        }
                        else
                            NeededQuantity = Used;
                    else
                        if (ExternalItem.Quantity > CurrentOffer.Quantity)
                            NeededQuantity = CurrentOffer.Quantity;
                        else
                            NeededQuantity = ExternalItem.Quantity;

                    // Add the order in external amap
                    int ExternalAmapOrderId = this.OtherAmapService.OrderExternalProduct(ExternalItem.AmapId, ExternalItem.OfferId, NeededQuantity);

                    // Keep trace of the order in ower database
                    ExternalOrder ExternalOrderItem = new ExternalOrder
                    {
                        AmapId = CurrentOffer.AmapId,
                        AmapExternalOrderId = ExternalAmapOrderId,
                        Price = CurrentOffer.Price,
                        Unit = CurrentOffer.Unit,
                        WantedQuantity = NeededQuantity,
                        ProvidedQuantity = -1,
                        RemainedQuantity = -1,
                        ProductId = this.ProductService.GetByExternalOffer(CurrentOffer).Id,
                        HarvestDate = DateTime.Now
                    };
                    this.ExternalOrderService.Add(ExternalOrderItem);

                    // We save the composition in the basket if such information is asked

                    if (ExternalItem.Unit > 0)
                    {
                        // Save basket line
                        ExternalComposition ExternalCompositionItem = new ExternalComposition
                        {
                            BasketId = BasketItem.Id,
                            ExternalOrderId = ExternalOrderItem.Id,
                            Quantity = ExternalItem.Unit
                        };
                        this.ExternalCompositionService.Add(ExternalCompositionItem);
                    }
                }
            }

            foreach (BasketOrder BasketOrder in this.BasketOrderService.GetCurrentWeekList())
                for (int i = 0; i < BasketOrder.Quantity; i++)
                    this.DeliveryService.Add(new Delivery
                    {
                        BasketId = BasketItem.Id,
                        ClientId = BasketOrder.ClientId
                    });
            foreach (Subscription Subscription in this.SubscriptionService.GetCurrentWeekList())
                for (int i = 0; i < Subscription.Quantity; i++)
                    this.DeliveryService.Add(new Delivery
                    {
                        BasketId = BasketItem.Id,
                        ClientId = Subscription.ClientId
                    });

            this.ExternalOrderService.Save();

            return View(Model);
        }

    }
}
